﻿/* 
 * Copyright (c) 2011 Jeff Zhang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, 
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, 
 * subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Json {
    public struct Number : IFormattable, IEquatable<Number>, IComparable<Number> {
        private int[] _bits;
        private bool _float;

        internal int Length {
            get { return _bits.Length; }
        }

        public bool Float {
            get { return _float; }
        }

        #region constructors

        private Number(bool @float, byte[] buffer) {
            _bits = new int[buffer.Length / 4];
            Buffer.BlockCopy(buffer, 0, _bits, 0, buffer.Length);
            _float = @float;
        }

        public Number(int value)
            : this(false, BitConverter.GetBytes(value)) {
        }

        public Number(long value)
            : this(false, BitConverter.GetBytes(value)) {
        }

        public Number(float value)
            : this(true, BitConverter.GetBytes(value)) {
        }

        public Number(double value)
            : this(true, BitConverter.GetBytes(value)) {
        }

        #endregion

        #region cast

        public static explicit operator int(Number number) {
            if (number._bits == null)
                return 0;

            byte[] buf = new byte[sizeof(int)];
            Buffer.BlockCopy(number._bits, 0, buf, 0, buf.Length);
            
            if (number.Float) {
                return (int)BitConverter.ToSingle(buf, 0);
            }

            return BitConverter.ToInt32(buf, 0);
        }

        public static explicit operator float(Number number) {
            if (number._bits == null)
                return 0f;


            byte[] buf = new byte[sizeof(float)];
            Buffer.BlockCopy(number._bits, 0, buf, 0, buf.Length);
            
            if (number.Float) {
                return BitConverter.ToSingle(buf, 0);
            }

            return (float)BitConverter.ToSingle(buf, 0);
        }

        public static explicit operator double(Number number) {
            if (number._bits == null)
                return 0.0;

            byte[] buf = new byte[sizeof(double)];
            Buffer.BlockCopy(number._bits, 0, buf, 0, buf.Length);
            if (number.Float) {
                return BitConverter.ToDouble(buf, 0);
            }
            
            return (double)BitConverter.ToInt64(buf, 0);
        }

        public static explicit operator long(Number number) {
            if (number._bits == null)
                return 0L;

            byte[] buf = new byte[sizeof(long)];
            Buffer.BlockCopy(number._bits, 0, buf, 0, buf.Length);
            if (number.Float) {
                return (long)BitConverter.ToDouble(buf, 0);
            }

            return BitConverter.ToInt64(buf, 0);
        }

        public static explicit operator Number(byte value) {
            return new Number(value);
        }

        public static explicit operator Number(short value) {
            return new Number(value);
        }

        public static explicit operator Number(ushort value) {
            return new Number(value);
        }

        public static explicit operator Number(int value) {
            return new Number(value);
        }

        public static explicit operator Number(uint value) {
            return new Number(value);
        }

        public static explicit operator Number(long value) {
            return new Number(value);
        }

        public static explicit operator Number(ulong value) {
            return new Number(value);
        }

        public static explicit operator Number(double value) {
            return new Number(value);
        }

        public static explicit operator Number(float value) {
            return new Number(value);
        }

        #endregion

        #region operators

        #region > operator

        public static bool operator >(Number num1, Number num2) {
            return num1.CompareTo(num2) > 0;
        }

        public static bool operator >(int num1, Number num2) {
            return num1.CompareTo(num2) > 0;
        }

        public static bool operator >(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) > 0;
        }

        public static bool operator >(long num1, Number num2) {
            return num1.CompareTo(num2) > 0;
        }

        public static bool operator >(Number num1, long num2) {
            return ((int)num1).CompareTo(num2) > 0;
        }

        public static bool operator >(float num1, Number num2) {
            return num1.CompareTo(num2) > 0;
        }

        public static bool operator >(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) > 0;
        }

        public static bool operator >(double num1, Number num2) {
            return num1.CompareTo(num2) > 0;
        }

        public static bool operator >(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) > 0;
        }

        #endregion

        #region < operator

        public static bool operator <(Number num1, Number num2) {
            return num1.CompareTo(num2) < 0;
        }

        public static bool operator <(int num1, Number num2) {
            return num1.CompareTo(num2) < 0;
        }

        public static bool operator <(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) < 0;
        }

        public static bool operator <(long num1, Number num2) {
            return num1.CompareTo(num2) < 0;
        }

        public static bool operator <(Number num1, long num2) {
            return ((long)num1).CompareTo(num2) < 0;
        }

        public static bool operator <(float num1, Number num2) {
            return num1.CompareTo(num2) < 0;
        }

        public static bool operator <(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) < 0;
        }

        public static bool operator <(double num1, Number num2) {
            return num1.CompareTo(num2) < 0;
        }

        public static bool operator <(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) < 0;
        }

        #endregion

        #region >= operator

        public static bool operator >=(Number num1, Number num2) {
            return num1.CompareTo(num2) >= 0;
        }

        public static bool operator >=(int num1, Number num2) {
            return num1.CompareTo(num2) >= 0;
        }

        public static bool operator >=(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) >= 0;
        }

        public static bool operator >=(long num1, Number num2) {
            return num1.CompareTo(num2) >= 0;
        }

        public static bool operator >=(Number num1, long num2) {
            return ((int)num1).CompareTo(num2) >= 0;
        }

        public static bool operator >=(float num1, Number num2) {
            return num1.CompareTo(num2) >= 0;
        }

        public static bool operator >=(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) >= 0;
        }

        public static bool operator >=(double num1, Number num2) {
            return num1.CompareTo(num2) >= 0;
        }

        public static bool operator >=(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) >= 0;
        }

        #endregion

        #region <= operator

        public static bool operator <=(Number num1, Number num2) {
            return num1.CompareTo(num2) <= 0;
        }

        public static bool operator <=(int num1, Number num2) {
            return num1.CompareTo(num2) <= 0;
        }

        public static bool operator <=(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) <= 0;
        }

        public static bool operator <=(long num1, Number num2) {
            return num1.CompareTo(num2) <= 0;
        }

        public static bool operator <=(Number num1, long num2) {
            return ((long)num1).CompareTo(num2) <= 0;
        }

        public static bool operator <=(float num1, Number num2) {
            return num1.CompareTo(num2) <= 0;
        }

        public static bool operator <=(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) <= 0;
        }

        public static bool operator <=(double num1, Number num2) {
            return num1.CompareTo(num2) <= 0;
        }

        public static bool operator <=(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) <= 0;
        }

        #endregion

        #region == operator

        public static bool operator ==(Number num1, Number num2) {
            return num1.CompareTo(num2) == 0;
        }

        public static bool operator ==(int num1, Number num2) {
            return num1.CompareTo(num2) == 0;
        }

        public static bool operator ==(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) == 0;
        }

        public static bool operator ==(long num1, Number num2) {
            return num1.CompareTo(num2) == 0;
        }

        public static bool operator ==(Number num1, long num2) {
            return num1.CompareTo((Number)num2) == 0;
        }

        public static bool operator ==(float num1, Number num2) {
            return num1.CompareTo(num2) == 0;
        }

        public static bool operator ==(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) == 0;
        }

        public static bool operator ==(double num1, Number num2) {
            return num1.CompareTo(num2) == 0;
        }

        public static bool operator ==(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) == 0;
        }


        #endregion

        #region != operator

        public static bool operator !=(Number num1, Number num2) {
            return num1.CompareTo(num2) != 0;
        }

        public static bool operator !=(int num1, Number num2) {
            return num1.CompareTo(num2) != 0;
        }

        public static bool operator !=(Number num1, int num2) {
            return ((int)num1).CompareTo(num2) != 0;
        }

        public static bool operator !=(long num1, Number num2) {
            return num1.CompareTo(num2) != 0;
        }

        public static bool operator !=(Number num1, long num2) {
            return ((long)num1).CompareTo(num2) != 0;
        }

        public static bool operator !=(float num1, Number num2) {
            return num1.CompareTo(num2) != 0;
        }

        public static bool operator !=(Number num1, float num2) {
            return ((float)num1).CompareTo(num2) != 0;
        }

        public static bool operator !=(double num1, Number num2) {
            return num1.CompareTo(num2) != 0;
        }

        public static bool operator !=(Number num1, double num2) {
            return ((double)num1).CompareTo(num2) != 0;
        }

        #endregion

        #endregion

        #region equals

        public bool Equals(Number other) {
            return this.CompareTo(other) == 0;
        }

        public override bool Equals(object obj) {
            if (obj is Number) {
                return this.Equals((Number)obj);
            }

            return false;
        }

        #endregion

        public static Number Parse(string numStr) {
            NumberParser parser = new NumberParser(numStr);

            Number number;
            if (parser.Parse(out number)) {
                return number;
            } else {
                throw new ArgumentException();
            }
        }

        public override int GetHashCode() {
            if (this.Length == 1) {
                return this._bits[0].GetHashCode();
            } else {
                if (Float) {
                    return ((double)this).GetHashCode();
                } else {
                    return ((long)this).GetHashCode();
                }
            }
        }

        public int CompareTo(Number other) {
            if (_bits == null) {
                if (other._bits == null) {
                    return 0;
                } else {
                    if (Float) {
                        if (this.Length == 1) {
                            float t = 0f;
                            return t.CompareTo((float)other);
                        } else {
                            double t = 0.0;
                            return t.CompareTo((double)other);
                        }
                    } else {
                        if (this.Length == 1) {
                            int t = (int)0;
                            return t.CompareTo((int)other);
                        } else {
                            long t = (long)0L;
                            return t.CompareTo((long)other);
                        }
                    }
                }
            } else {
                if (other._bits == null) {
                    if (Float) {
                        if (this.Length == 1) {
                            float t = (float)this;
                            return t.CompareTo(0f);
                        } else {
                            double t = (double)this;
                            return t.CompareTo(0.0);
                        }
                    } else {
                        if (this.Length == 1) {
                            int t = (int)this;
                            return t.CompareTo(0);
                        } else {
                            long t = (long)this;
                            return t.CompareTo(0L);
                        }
                    }
                } else {
                    if (Float) {
                        if (this.Length == 1) {
                            float t = (float)this;
                            return t.CompareTo((float)other);
                        } else {
                            double t = (double)this;
                            return t.CompareTo((double)other);
                        }
                    } else {
                        if (this.Length == 1) {
                            int t = (int)this;
                            return t.CompareTo((int)other);
                        } else {
                            long t = (long)this;
                            return t.CompareTo((long)other);
                        }
                    }
                }
            }
        }

        public override string ToString() {
            if (Float) {
                if (this.Length == 1) {
                    float t = (float)this;
                    return t.ToString(CultureInfo.InvariantCulture);
                } else {
                    double t = (double)this;
                    return t.ToString(CultureInfo.InvariantCulture);
                }
            } else {
                if (this.Length == 1) {
                    int t = (int)this;
                    return t.ToString(CultureInfo.InvariantCulture);
                } else {
                    long t = (long)this;
                    return t.ToString(CultureInfo.InvariantCulture);
                }
            }
        }

        public string ToString(string format, IFormatProvider formatProvider) {
            if (Float) {
                if (this.Length == 1) {
                    float t = (float)this;
                    return t.ToString(format, formatProvider);
                } else {
                    double t = (double)this;
                    return t.ToString(format, formatProvider);
                }
            } else {
                if (this.Length == 1) {
                    int t = (int)this;
                    return t.ToString(format, formatProvider);
                } else {
                    long t = (long)this;
                    return t.ToString(format, formatProvider);
                }
            }
        }

        public TypeCode GetTypeCode() {
            if (Float) {
                if (this.Length == 1) {
                    return TypeCode.Single;
                } else {
                    return TypeCode.Double;
                }
            } else {
                if (this.Length == 1) {
                    return TypeCode.Int32;
                } else {
                    return TypeCode.Int64;
                }
            }
        }

        private class NumberParser {
            int cursor = 0;
            int length;
            string raw;

            public NumberParser(string val) {
                raw = val.Trim();
                length = raw.Length;
            }

            bool EOF {
                get { return cursor >= length; }
            }


            public bool Parse(out Number result) {
                result = new Number();
                if (length == 0) {
                    return false;
                }

                CharBuffer buffer = new CharBuffer(32);

                char c = raw[cursor++];

                if (char.IsNumber(c) || c == plus || c == minus) {
                    buffer.Push(c);

                    if (c == plus || c == minus) {
                        if (EOF) {
                            return false;
                        } else {
                            c = raw[cursor++];
                            if (char.IsNumber(c)) {
                                buffer.Push(c);
                            } else {
                                return false;
                            }
                        }
                    }
                } else {
                    return false;
                }

                bool hasdot = false;
                //bool hase = false;

                while (!EOF) {
                    c = raw[cursor++];

                    if (char.IsNumber(c)) {
                        buffer.Push(c);
                    } else if (c == dot && !hasdot) {
                        buffer.Push(c);
                        hasdot = true;
                    } else {
                        return false;
                    }
                }

                string val = buffer.GetString();
                if (hasdot) {
                    result = new Number(double.Parse(val));
                } else {
                    int v;
                    if (int.TryParse(val, out v)) {
                        result = new Number(v);
                    } else {
                        long vx;
                        if (long.TryParse(val, out vx)) {
                            result = new Number(vx);
                        } else {
                            return false;
                        }
                    }
                }

                return true;
            }

            //public bool Parse(out Number result) {
            //    result = new Number();
            //    if (length == 0) {
            //        return false;
            //    }

            //    CharBuffer buffer = new CharBuffer(32);

            //    char c = raw[cursor++];
            //    bool ishex = false;
            //    bool isoct = false;
            //    //bool hexminus = false;

            //    if (char.IsNumber(c) || c == plus || c == minus) {
            //        buffer.Push(c);

            //        if (c == '0') {
            //            if (!EOF) {
            //                c = raw[cursor++];
            //                if (c == hex || c == HEX) {
            //                    ishex = true;
            //                    c = char.ToLower(c);
            //                } else if (!char.IsNumber(c)) {
            //                    return false;
            //                } else {
            //                    isoct = true;

            //                }
            //                buffer.Push(c);
            //            }
            //        } else if (c == plus || c == minus) {
            //            if (EOF) {
            //                return false;
            //            } else {
            //                c = raw[cursor++];
            //                if (char.IsNumber(c)) {
            //                    buffer.Push(c);
            //                    if (c == '0') {
            //                        if (!EOF) {
            //                            c = raw[cursor++];

            //                            if (c == hex || c == HEX) {
            //                                ishex = true;
            //                                c = char.ToLower(c);
            //                            } else if (!char.IsNumber(c)) {
            //                                return false;
            //                            } else {
            //                                isoct = true;
            //                            }

            //                            buffer.Push(c);
            //                        }
            //                    }
            //                } else {
            //                    return false;
            //                }
            //            }
            //        }
            //    } else {
            //        return false;
            //    }

            //    bool hasdot = false;
            //    bool hase = false;

            //    while (!EOF) {
            //        c = raw[cursor++];

            //        if (char.IsNumber(c)) {
            //            buffer.Push(c);
            //        } else if (c == dot && !hasdot) {
            //            buffer.Push(c);
            //            hasdot = true;
            //        } else if (c == comma) {
            //            continue;
            //        } else if ((c == e || c == E) && !hase && !ishex) {
            //            if (EOF) {
            //                return false;
            //            }

            //            char cx = raw[cursor++];
            //            if (cx == plus || cx == minus) {
            //                hase = true;
            //                buffer.Push(c);
            //                buffer.Push(cx);
            //            } else {
            //                return false;
            //            }
            //        } else if (ishex && ishexletter(c)) {
            //            buffer.Push(c);
            //        } else{
            //            return false;
            //        }
            //    }

            //    string val = buffer.GetString();
            //    if (hase || hasdot) {
            //        result = new Number(double.Parse(val));
            //    } else {
            //        if (ishex) {
            //            bool f = false;
            //            if (val.StartsWith("-")) {
            //                val = val.Substring(1).Replace("0x", "");
            //                f = true;
            //            } else if (val.StartsWith("+")) {
            //                val = val.Substring(1).Replace("0x", "");
            //            } else {
            //                val = val.Replace("0x", "");
            //            }

            //            int v;
            //            if (int.TryParse(val, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out v)) {
            //                if (f) {
            //                    result = new Number(-v);
            //                } else {
            //                    result = new Number(v);
            //                }
            //            } else {
            //                long vx;
            //                if (long.TryParse(val, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out vx)) {
            //                    if (f) {
            //                        result = new Number(-vx);
            //                    } else {
            //                        result = new Number(vx);
            //                    }
            //                } else {
            //                    return false;
            //                }
            //            }
            //        } else {
            //            int v;
            //            if (int.TryParse(val, out v)) {
            //                result = new Number(v);
            //            } else {
            //                long vx;
            //                if (long.TryParse(val, out vx)) {
            //                    result = new Number(vx);
            //                } else {
            //                    return false;
            //                }
            //            }
            //        }
            //    }

            //    return true;
            //}

            bool ishexletter(char c) {
                const char A = 'A';
                const char B = 'B';
                const char C = 'C';
                const char D = 'D';
                const char E = 'E';
                const char F = 'F';

                c = char.ToUpper(c);
                return c == A || c == B || c == C || c == D || c == E || c == F;
            }

            const char dot = '.';
            const char comma = ',';
            const char e = 'e';
            const char E = 'E';
            const char plus = '+';
            const char minus = '-';
            const char hex = 'x';
            const char HEX = 'X';

        }
    }
}
